package com.vdong.common.oauth.common.authorize.util;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vdong.kpay.basics.dubbo.api.backend.StoreService;
import com.vdong.kpay.basics.dubbo.pojo.auth.AuthUser;
import com.vdong.kpay.basics.dubbo.pojo.backend.StoreInfo;
import com.vdong.member.account.facade.AccountSystemUserFacadeService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @author 22990
 */
@Component
public class JwtTokenUtil implements Serializable {

    private static final long serialVersionUID = -3301605591108950415L;

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";
    private static final String CLAIM_KEY_AUTHORITY = "authority";
    private static final String CLAIM_KEY_CREDENTIALS = "credentials";
    private static final String CLAIM_KEY_STORE_ID = "storeid";
    private static final String CLAIM_KEY_USER_ID = "userid";
    private static final String CLAIM_KEY_PLAT_ID = "platid";
    private static final String CLAIM_KEY_SYSUSER_ID = "sysUserId";
    public static final Long LAMB_TOKEN_TIME = 18000000L;
    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    @Value("${sourcePlatformId}")
    private Long sourcePlatformId;

    @Reference
    private AccountSystemUserFacadeService accountSystemUserFacadeService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Reference(version = "1.0.0")
    private StoreService storeService;


    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    public String getAuthorityFromToken(String token) {
        String authorities;
        try {
            final Claims claims = getClaimsFromToken(token);
            authorities = claims.get(CLAIM_KEY_AUTHORITY, String.class);
        } catch (Exception e) {
            authorities = null;
        }
        return authorities;
    }

    public String getCredentialsFromToken(String token) {
        String authorities;
        try {
            final Claims claims = getClaimsFromToken(token);
            authorities = claims.get(CLAIM_KEY_CREDENTIALS, String.class);
        } catch (Exception e) {
            authorities = null;
        }
        return authorities;
    }

    public Long getUserIdFromToken(String token) {
        Long userId;
        try {
            final Claims claims = getClaimsFromToken(token);
            userId = Long.valueOf(claims.get(CLAIM_KEY_USER_ID, String.class));
        } catch (Exception e) {
            userId = null;
        }
        return userId;
    }

    public Long getStoreIdFromToken(String token) {
        Long storeId;
        try {
            final Claims claims = getClaimsFromToken(token);
            storeId = Long.valueOf(claims.get(CLAIM_KEY_STORE_ID, String.class));
        } catch (Exception e) {
            storeId = null;
        }
        return storeId;
    }

    private Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    private Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }

    public String generateToken() {
        AuthUser authUser = SecurityUtil.getAuthUser();
        if (null == authUser) {
            return "";
        }
        Map<String, Object> claims = new HashMap<>(16);
        StringBuilder sb = new StringBuilder();
        Collection<GrantedAuthority> authorities = authUser.getAuthorities();
        if (!CollectionUtils.isEmpty(authorities)) {
            Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
            while (iterator.hasNext()) {
                GrantedAuthority next = iterator.next();
                String authority = next.getAuthority();
                sb.append(",").append(authority);

            }
            claims.put(CLAIM_KEY_AUTHORITY, sb.toString().replaceFirst(",", ""));
        }
        claims.put(CLAIM_KEY_USERNAME, authUser.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        claims.put(CLAIM_KEY_CREDENTIALS, authUser.getPassword());
        Long storeId = authUser.getStoreId();
        StoreInfo store = null;
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("platformInsideId",storeId);
        Long userId = authUser.getUserId();
        if (null != userId) {
            //SAAS
            claims.put(CLAIM_KEY_USER_ID, userId.toString());
            jsonObject.put("platformInsideId",userId);
            jsonObject.put("userType",4);
        }
        if (null != storeId) {
            //店铺
            claims.put(CLAIM_KEY_STORE_ID, storeId.toString());
            jsonObject.put("platformInsideId",storeId);
            jsonObject.put("userType",2);
//            store = storeService.getStoreBaseInfo(storeId);
        }

//        claims.put(CLAIM_KEY_PLAT_ID,sourcePlatformId);

        jsonObject.put("sourcePlatformId",sourcePlatformId);

        String accountSystemPeddleUserInfo = accountSystemUserFacadeService.getAccountSystemPeddleUserInfo(jsonObject.toJSONString());
        JSONObject userInfo = JSONObject.parseObject(accountSystemPeddleUserInfo);
        Boolean flag = (Boolean) userInfo.get("success");
        String userStr = "";
        String sysUserId = "";
        if(!flag){
            jsonObject.put("systemUserName",authUser.getUsername());
            String addresult = accountSystemUserFacadeService.addAccountSystemPeddleUser(jsonObject.toJSONString());
            JSONObject addUser = JSONObject.parseObject(addresult);
            String user = (String)addUser.get("data");
            JSONObject userObject = JSON.parseObject(user);
            if(null!=store){
                userObject.put("logoUrl",store.getBucketDomain()+"/"+store.getLogoUrl());
            }
            userStr = userObject.toJSONString();
            sysUserId = (String) userObject.get("sysUserId");
            claims.put(CLAIM_KEY_SYSUSER_ID,sysUserId);
        }else{
            String user = (String)userInfo.get("data");
            JSONObject userObject = JSON.parseObject(user);
            if(null!=store){
                userObject.put("logoUrl",store.getBucketDomain()+"/"+store.getLogoUrl());
            }
            userStr = userObject.toJSONString();
            sysUserId = (String) userObject.get("sysUserId");
            claims.put(CLAIM_KEY_SYSUSER_ID,sysUserId);
        }
        System.out.println(userStr);
        String token = generateToken(claims);
        System.out.println(token);
        stringRedisTemplate.opsForValue().set(token,userStr,LAMB_TOKEN_TIME.longValue(), TimeUnit.MILLISECONDS);
        return token;
    }



    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && !isTokenExpired(token);
    }

    public String refreshToken(String token) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            claims.put(CLAIM_KEY_CREATED, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    public Boolean validateToken(String token) {
        final String username = getUsernameFromToken(token);
        final Date created = getCreatedDateFromToken(token);
        //final Date expiration = getExpirationDateFromToken(token);
        return !isTokenExpired(token);
    }
}